package com.hh.job;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.DateTime;
import cn.hutool.core.date.DateUtil;
import cn.hutool.core.text.StrPool;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.hh.entity.domain.UserCollection;
import com.hh.entity.enums.ThumbTypeEnum;
import com.hh.mapper.BlogMapper;
import com.hh.service.UserCollectionService;
import com.hh.utils.RedisKeyUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;

/**
 * com.hh.job
 * 定时将Redis中的点赞数据同步到数据库
 *
 * @author hanhui
 * @version 1.0
 * @date 2025/8/1 12:30
 **/
@Component
@Slf4j
public class SyncCollect2DBJob {

    @Resource
    private RedisTemplate<String, Object> redisTemplate;
    @Resource
    UserCollectionService userCollectionService;
    @Resource
    BlogMapper blogMapper;

    @Scheduled(fixedRate = 10000)
    @Transactional(rollbackFor = Exception.class)
    public void run() {
        log.info("定时任务开始执行，将Redis中的收藏数据同步到数据库");
        DateTime nowDate = DateUtil.date();
        int second = (DateUtil.second(nowDate) / 10 - 1) * 10;
        if (second == -10) {
            second = 50;
            nowDate = DateUtil.offsetMinute(nowDate, -1);
        }
        String date = DateUtil.format(nowDate, "HH:mm") + ":" + second;
        log.info("当前时间：{}", date);
        syncCollect2DBByDate(date);
        log.info("定时任务执行结束，将Redis中的收藏数据同步到数据库");
    }

    /**
     * 根据日期将收藏数据从Redis同步到数据库
     *
     * @param date 日期字符串，用于指定要同步的数据日期
     */
    @Async("taskExecutor")
    @Transactional(rollbackFor = Exception.class)
    public void syncCollect2DBByDate(String date) {
        // 获取指定日期的临时收藏数据在Redis中的键
        String tempCollectKey = RedisKeyUtil.getTempCollectKey(date);
        // 从Redis中获取所有收藏数据
        Map<Object, Object> entries = redisTemplate.opsForHash().entries(tempCollectKey);
        // 检查Redis中的收藏数据是否为空
        boolean empty = CollUtil.isEmpty(entries);
        // 用于存储博客ID和对应的收藏数量映射
        Map<Long, Long> blogThumbMap = new HashMap<>();
        // 如果Redis中没有数据，直接返回
        if (empty) {
            return;
        }
        // 创建收藏列表，用于批量插入数据库
        ArrayList<UserCollection> collectList = new ArrayList<>();
        // 创建Lambda查询条件构造器
        LambdaQueryWrapper<UserCollection> queryWrapper = new LambdaQueryWrapper<>();
        // 是否有取消收藏操作
        boolean hasDecr = false;
        // 遍历Redis中的收藏数据
        for (Object userIdBlogIdObj : entries.keySet()) {
            try {
                // 将键转换为字符串格式
                String userIdBlogId = (String) userIdBlogIdObj;
                // 分割用户ID和博客ID
                String[] userIdAndBlogId = userIdBlogId.split(StrPool.COLON);
                Long userId = Long.valueOf(userIdAndBlogId[0]);
                Long blogId = Long.valueOf(userIdAndBlogId[1]);
                // 获取收藏类型
                Integer thumbType = Integer.valueOf(entries.get(userIdBlogId).toString());
                // 根据收藏类型执行不同操作
                if (thumbType == ThumbTypeEnum.INCR.getValue()) {
                    // 如果是新增收藏，创建收藏对象并添加到列表
                    UserCollection collect = new UserCollection();
                    collect.setUserId(userId);
                    collect.setBlogId(blogId);
                    collectList.add(collect);
                    // 更新博客收藏数量映射
                    blogThumbMap.put(blogId, blogThumbMap.getOrDefault(blogId, 0L) + 1L);
                } else if (thumbType == ThumbTypeEnum.DECR.getValue()) {
                    // 如果是取消收藏，标记需要删除，并构建查询条件
                    hasDecr = true;
                    queryWrapper.or()
                            .eq(UserCollection::getUserId, userId)
                            .eq(UserCollection::getBlogId, blogId);
                    // 更新博客收藏数量映射
                    blogThumbMap.put(blogId, blogThumbMap.getOrDefault(blogId, 0L) - 1L);
                } else {
                    // 如果是其他类型，记录警告日志并跳过
                    if (thumbType == ThumbTypeEnum.NON.getValue()) {
                        log.warn("数据异常:{}", userId + ":" + blogId + ":" + thumbType);
                    }
                    continue;
                }
            } catch (NumberFormatException e) {
                log.error("解析Redis中的收藏数据失败，userIdBlogId: {}", userIdBlogIdObj, e);
            }
        }
        // 批量保存收藏数据
        userCollectionService.saveBatch(collectList);
        // 如果需要删除，执行删除操作
        if (hasDecr) {
            userCollectionService.remove(queryWrapper);
        }
        // 如果有博客收藏数量变化，批量更新博客收藏数
        if (!blogThumbMap.isEmpty()) {
            blogMapper.batchUpdateCollectCount(blogThumbMap);
        }
        // 删除Redis中的临时键
        deleteTempThumbKey(tempCollectKey);
        // 记录任务执行完成日志
        log.info("定时任务结束执行，将Redis中的收藏数据同步到数据库");
    }


    @Async
    public void deleteTempThumbKey(String tempCollectKey) {
        log.info("异步任务开始");
        redisTemplate.delete(tempCollectKey);
        log.info("异步任务结束");
    }
}
